package cursor

import (
	"context"

	v1 "code.byted.org/bytertc/whiteboard_server/biz/api/pb/v1"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/biz_error"
	"code.byted.org/bytertc/whiteboard_server/biz/service/action"
	"code.byted.org/bytertc/whiteboard_server/biz/service/action/base"
	"github.com/pkg/errors"
	"google.golang.org/protobuf/proto"
)

func NewAction(ctx context.Context, baseAct base.Action, raw *v1.Action) (act action.Action, err error) {
	switch raw.Type {
	case v1.ActionType_kMoveCursor:
		act = &moveCursorAction{
			Action:    baseAct,
			rawAction: raw,
		}
	case v1.ActionType_kChangeCursorState:
		state := &v1.ChangeCursorState{}
		err = proto.Unmarshal(raw.Data, state)
		if err != nil {
			return nil, biz_error.DecodeError.Wrap(errors.WithStack(err))
		}
		act = &changeCursorStateAction{
			Action:    baseAct,
			rawAction: raw,
			state:     state,
		}

	default:
		return nil, biz_error.DecodeError.Wrap(errors.Errorf("no such cursor action %d", raw.Type))
	}

	return act, nil
}

type moveCursorAction struct {
	base.Action
	rawAction *v1.Action
	penPoints *v1.PenPoint
}

func (m *moveCursorAction) GetActionId() int32 {
	return m.rawAction.GetActionId()
}

func (m *moveCursorAction) GetObjId() string {
	return m.rawAction.GetObjId()
}

func (m *moveCursorAction) ConflictObjects() []string {
	return nil
}

func (m *moveCursorAction) GetOperator() string {
	return m.Operator
}

func (m *moveCursorAction) IsAtomic() bool {
	return true
}
func (m *moveCursorAction) Execute(ctx context.Context) error {
	state, err := m.Repo.GetMyCursorState(ctx, m, m.Operator)
	if err != nil {
		return err
	}
	if state == nil {
		return biz_error.UnknownError.Wrap(errors.Errorf("state is nil"))
	}
	if state.ShowState == v1.UserCursorInfo_kHide {
		return biz_error.PacketReject.Wrap(errors.Errorf("cursor is hide"))
	}

	return nil
}

func (m *moveCursorAction) RawActions() []*v1.Action {
	return []*v1.Action{m.rawAction}
}

type changeCursorStateAction struct {
	base.Action
	rawAction *v1.Action
	state     *v1.ChangeCursorState
}

func (c *changeCursorStateAction) GetActionId() int32 {
	return c.rawAction.GetActionId()
}

func (c *changeCursorStateAction) GetObjId() string {
	return c.rawAction.GetObjId()
}

func (c *changeCursorStateAction) ConflictObjects() []string {
	return nil
}

func (c *changeCursorStateAction) GetOperator() string {
	return c.Operator
}

func (c *changeCursorStateAction) IsAtomic() bool {
	return true
}
func (c *changeCursorStateAction) Execute(ctx context.Context) error {
	err := c.Repo.ChangeCursorState(ctx, c, c.Operator, c.state)
	return err
}

func (c *changeCursorStateAction) RawActions() []*v1.Action {
	return []*v1.Action{c.rawAction}
}
